package com.tianji.learning.handle;

import cn.hutool.core.collection.CollUtil;
import com.tianji.learning.constants.LearningConstant;
import com.tianji.learning.constants.RedisConstant;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.po.PointsBoardSeason;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.tianji.learning.utils.TableInfoContext;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Component
@Slf4j
@RequiredArgsConstructor
public class CreatePointsBoardTableHandle {
    private final IPointsBoardSeasonService seasonService;
    private final IPointsBoardService boardService;
    private final StringRedisTemplate redisTemplate;

    /**
     * 月初生成上赛季表
     */
    @XxlJob("createPointsBoardTable")
    public void createPointsBoardTableOfLastSeason(){
        //根据上月份查询上赛季信息
        log.debug("上赛季信息执行了。。。。。");
        Integer season = getSeason();
        if(season == null){
            return;
        }
        //生成上赛季的赛季信息，便于上赛季信息的持久化
        boardService.createPointsBoardTable(season);
    }

    /**
     * 定时持久化赛季信息，并且清理redis数据
     */
    @XxlJob("savePointsBoard2DB")
    @Transactional
    public void savePointsBoard2DB(){
        log.debug("开始持久化数据。。。。。。。。");
        //1.准备获取动态表名
        Integer season = getSeason();
        if(season == null){
            return;
        }
        TableInfoContext.setInfo(LearningConstant.POINTS_BOARD_TABLE_PREFIX + season);
        log.debug("持久化的表名：{}。。。。。",LearningConstant.POINTS_BOARD_TABLE_PREFIX + season);
        //2.获取redis数据
        String format = LocalDateTime.now().minusMonths(1).format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstant.POINTS_BOARDS_KEY_PREFIX + format;
        //String key = RedisConstant.POINTS_BOARDS_KEY_PREFIX + "202302";
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, -1);
        if(CollUtil.isEmpty(typedTuples)){
            return;
        }

        //3.把数据持久化进mysql对应分表
        //由于数据量过大，所以一次1000的写入
        /*
          还有一个要点就是因为数据量太大，一个任务实例可能耗费时间过久
          这时我们可以采取xxljob的分片广播，把数据分给俩个或以上的实例执行
         */
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();
        /*
          shardIndex代表任务的序号
          shardTotal代表分片数量，即执行任务的服务数量
         */
        int pageNo = shardIndex + 1;
        int pageSize = 1000;
        while (true){
            List<PointsBoard> pointsBoards = boardService.pageQueryCurrent(key, pageNo, pageSize);
            if(pointsBoards.isEmpty()){
                break;
            }
            /*
              3.1 由于在分的表里没有如rank season等值 所以在封装的pointsBoards要把这些去掉
              并且把rank赋值给id，因为id就是排名
             */
            for (PointsBoard pointsBoard : pointsBoards) {
                pointsBoard.setId(Long.valueOf(pointsBoard.getRank()));
                pointsBoard.setRank(null);
            }
            boardService.saveBatch(pointsBoards);
            pageNo += shardTotal;
        }


        //5.清掉局部线程值
        TableInfoContext.remove();
    }

    private Integer getSeason() {
        LocalDate now = LocalDate.now().minusMonths(1);
        PointsBoardSeason one = seasonService.lambdaQuery()
                .le(PointsBoardSeason::getBeginTime, now)
                .ge(PointsBoardSeason::getEndTime, now)
                .one();
        if(one == null){
            return null;
        }
        return one.getId();
    }
    @XxlJob("clearPointsBoardFromRedis")
    public void clearPointsBoardFromRedis(){
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);
        // 2.计算key
        String format = LocalDateTime.now().minusMonths(1).format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstant.POINTS_BOARDS_KEY_PREFIX + format;
        // 3.删除
        redisTemplate.unlink(key);
        /**
         * 清理redis数据,unlink比较适合大量删除的场景
         */
        //redisTemplate.delete(key);
    }
}
